import os from 'os'
import fs from 'fs'
import * as sinon from 'sinon'
import {expect} from 'chai'
import path from 'path'
import platform from '../../src/platform'
import * as download from '../../src/tool/download'
import * as cache from '../../src/tool/cache'
import * as which from '../../src/tool/which'
import * as extract from '../../src/tool/extract'
import {install} from '../../src/tool/install'
import * as step from '../../src';

describe('installing tool', () => {
    let mkdirSyncStub: sinon.SinonStub
    let decompressFuncStub: sinon.SinonStub
    let stubRequestURL: sinon.SinonStub
    let stubDownload: sinon.SinonStub
    let stubWhich: sinon.SinonStub
    let stubCacheFile: sinon.SinonStub
    let stubCacheDir: sinon.SinonStub
    let stubRmSync: sinon.SinonStub
    let stubPathJoin: sinon.SinonStub
    let stubExtract: sinon.SinonStub
    let stepExecStub: sinon.SinonStub;
    const downloadUrl ='http://the.flow.tool.registry.com/sometool/sometool-1.0.0-linux-x64'

    beforeEach(() => {
        process.env['FLOW_TOOL_REGISTRY_URL'] = 'http://the.flow.tool.registry.com'
        sinon.stub(platform, 'getArch').withArgs().returns('x64')
        sinon.stub(platform, 'getPlatform').withArgs().returns('linux')
        stubRequestURL = sinon.stub(download, 'requestURL')
        stubDownload = sinon.stub(download, 'download')
        stubCacheFile = sinon.stub(cache, 'cacheFile')
        stubCacheDir = sinon.stub(cache, 'cacheDir')
        stubWhich = sinon.stub(which, 'which')
        mkdirSyncStub = sinon.stub(fs, 'mkdirSync')
        stubRmSync = sinon.stub(fs, 'rmSync')
        stubPathJoin = sinon.stub(path, 'join')
        stubExtract = sinon.stub(extract, 'extract')
        decompressFuncStub = sinon.stub()
        stepExecStub = sinon.stub(step.exec, 'call')
    })

    afterEach(() => {
        sinon.restore()
    })

    it('should return cached file path if present', async() => {
        stubWhich.withArgs('sometool', '1.0.0').returns(Promise.resolve('/some/cached/folder'))
        
        const installPath = await install('sometool', '1.0.0')

        expect(installPath).equals('/some/cached/folder')
    })

    it('should return cached file path if copied from local cache', async () => {
        stubWhich.withArgs('sometool', '1.0.0').onFirstCall().returns(Promise.resolve(''))
        stubWhich.withArgs('sometool', '1.0.0').onSecondCall().returns(Promise.resolve('/some/cached/folder'))
        givenLocalCacheServerIsAvailable()

        const installPath = await install('sometool', '1.0.0')

        expect(installPath).equals('/some/cached/folder')
    })

    it('should download file from tool registry and cache it', async () => {
        givenLocalCacheServerIsNotAvailable()
        stubDownload.withArgs(downloadUrl, {}).returns(Promise.resolve('/some/downloaded/file'))
        stubCacheFile.withArgs(
                '/some/downloaded/file',
                'sometool-1.0.0',
                'sometool',
                '1.0.0'
            ).returns(Promise.resolve('/some/cached/folder'))

        const installPath = await install('sometool', '1.0.0')

        sinon.assert.calledOnce(stubRmSync)
        expect(installPath).equals('/some/cached/folder')
    })

    it('should download zip file from tool registry and extract it', async () => {
        givenLocalCacheServerIsNotAvailable()
        stubDownload.withArgs(downloadUrl + '.tar.gz', {}).returns(Promise.resolve('/some/downloaded/file.tar.gz'))
        stubPathJoin.withArgs(sinon.match.any, sinon.match.any).returns('/tmp/someuuid')
        stubCacheDir.withArgs('/tmp/someuuid', 'sometool', '1.0.0').returns(Promise.resolve('/some/cached/folder'))

        const installPath = await install('sometool', '1.0.0', 'tar.gz')

        sinon.assert.calledTwice(stubRmSync)
        sinon.assert.calledWith(stubExtract, '/some/downloaded/file.tar.gz', '/tmp/someuuid', {strip: 1})
        expect(installPath).equals('/some/cached/folder')
    })

    it('should download tar file from tool registry and use tar to extract when in k8s cluster', async () => {
        givenLocalCacheServerIsNotAvailable()
        stubDownload.withArgs(downloadUrl + '.tar.gz', {}).returns(Promise.resolve('/some/downloaded/file.tar.gz'))
        stubPathJoin.withArgs(sinon.match.any, sinon.match.any).returns('/tmp/someuuid')
        stubCacheDir.withArgs('/tmp/someuuid', 'sometool', '1.0.0').returns(Promise.resolve('/some/cached/folder'))

        process.env["NODEGROUP_TYPE"] = "K8S"
        stepExecStub.returns(Promise.resolve(0));
        const installPath = await install('sometool', '1.0.0', 'tar.gz')

        sinon.assert.calledTwice(stubRmSync)
        sinon.assert.notCalled(stubExtract)
        expect(installPath).equals('/some/cached/folder')

        delete process.env["NODEGROUP_TYPE"]
    })

    it('should download tar file from tool registry and use tar to extract but failed, fallback to decompress', async () => {
        givenLocalCacheServerIsNotAvailable()
        stubDownload.withArgs(downloadUrl + '.tar.gz', {}).returns(Promise.resolve('/some/downloaded/file.tar.gz'))
        stubPathJoin.withArgs(sinon.match.any, sinon.match.any).returns('/tmp/someuuid')
        stubCacheDir.withArgs('/tmp/someuuid', 'sometool', '1.0.0').returns(Promise.resolve('/some/cached/folder'))

        process.env["NODEGROUP_TYPE"] = "K8S"
        stepExecStub.returns(Promise.resolve(1));
        const installPath = await install('sometool', '1.0.0', 'tar.gz')

        sinon.assert.calledTwice(stubRmSync)
        sinon.assert.calledWith(stubExtract, '/some/downloaded/file.tar.gz', '/tmp/someuuid', {strip: 1})
        expect(installPath).equals('/some/cached/folder')

        delete process.env["NODEGROUP_TYPE"]
    })

    function givenLocalCacheServerIsNotAvailable() {
        stubRequestURL
            .withArgs(
                'http://localhost:11516/restore?toolname=sometool&version=1.0.0&arch=x64'
            ).throws('ECONNREFUSED')
    }

    function givenLocalCacheServerIsAvailable() {
        stubRequestURL
            .withArgs(
                'http://localhost:11516/restore?toolname=sometool&version=1.0.0&arch=x64'
            ).returns('success')
    }
})
